#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

from abc import ABC, abstractmethod

from antdb.common.const import RestoreAction
from antdb.common.models import RestoreProgress
from antdb.common.util.antdb_common_utils import AntDBCommonUtils
from antdb.restore.antdb_restore_base import AntDBRestoreBase
from antdb.restore.antdb_restore_service import AntDBRestoreService
from common.const import ExecuteResultEnum, RepositoryDataTypeEnum
from common.logger import Logger
from common.number_const import NumberConst

LOGGER = Logger().get_logger("antdb.log")


class AntDBClusterRestoreAbstract(AntDBRestoreBase, ABC):
    """AntDB集群实例恢复任务执行抽象类
    """

    def __init__(self, pid, job_id, sub_job_id, param_dict):
        super().__init__(pid, job_id, sub_job_id, param_dict)

    @abstractmethod
    def exec_pre_subtask(self):
        """执行恢复前子任务
        """
        LOGGER.info("Start executing restore prepare subtask ...")
        cache_path = AntDBRestoreService.get_cache_mount_path(self.param_dict)

        # 1.恢复前检查目标实例是否已停止
        if not AntDBRestoreService.pre_check_db_not_running(self.pid, self.param_dict, cache_path, is_cluster=True):
            return
        # 2.检查数据库端口是否被占用
        if not AntDBRestoreService.check_db_port_not_listen(self.pid, self.param_dict, cache_path, is_cluster=True):
            return

        # 如果以Patroni部署，进行日志恢复，检查patroni.yml是否配置相关参数
        restore_time = self.param_dict.get("job", {}).get("extendInfo", {}).get("restoreTimestamp")
        if restore_time and not AntDBRestoreService.check_adbhamgr_pit_conf(self.pid, self.param_dict, cache_path):
            return

        job_dict = self.param_dict.get("job", {})
        copies = AntDBRestoreService.parse_copies(job_dict)
        copy_mount_path = AntDBRestoreService.get_copy_mount_paths(
            copies[0], RepositoryDataTypeEnum.DATA_REPOSITORY.value)[0]
        tgt_location = job_dict.get("extendInfo", {}).get("targetLocation")
        # 恢复到原位置
        if tgt_location == "original":
            # 3.检查表空间的目录是否存在，存在则检查表空间所属用户是否与data目录一致，不存在则创建
            if not AntDBRestoreService.pre_check_table_space_path(self.pid, self.job_id, copy_mount_path):
                return
            AntDBCommonUtils.write_progress_info(cache_path, RestoreAction.QUERY_RESTORE, RestoreProgress(
                progress=10, message="completed"))
            LOGGER.info(f"Executing restore prepare subtask success, target location is {tgt_location}.")
            AntDBRestoreService.record_task_result(self.pid, "Executing restore prepare subtask success",
                                                   code=ExecuteResultEnum.SUCCESS.value)
            return

        # 3.检查副本的数据库版本和目标实例版本是否匹配
        if not AntDBRestoreService.pre_check_version_is_matched(
                self.pid, self.param_dict, cache_path, is_cluster=True):
            return

        # 4.检查目标实例安装目录所属用户是否是副本数据所属的系统用户
        if not AntDBRestoreService.pre_check_os_user_is_same(
                self.pid, self.param_dict, cache_path, is_cluster=True):
            return

        # 5.检查副本数据所属的系统用户是否可读写目标实例数据目录的父目录
        if not AntDBRestoreService.pre_check_target_data_path_is_rw(
                self.pid, self.param_dict, cache_path, is_cluster=True):
            return

        # 6.检查表空间的目录是否存在，建存在则检查表空间所属用户是否与data目录一致，不存在则创
        if not AntDBRestoreService.pre_check_table_space_path(self.pid, self.job_id, copy_mount_path):
            return

        # 7.清理残余patroni日志恢复拷贝数据目录
        AntDBRestoreService.delete_copy_dir_for_patroni(self.param_dict)
        LOGGER.info(f"Execute restore prepare subtask success, target location is {tgt_location}")
        AntDBCommonUtils.write_progress_info(cache_path, RestoreAction.QUERY_RESTORE, RestoreProgress(
            progress=10, message="completed"))
        AntDBRestoreService.record_task_result(self.pid, "Executing restore prepare subtask success",
                                               code=ExecuteResultEnum.SUCCESS.value)

    @abstractmethod
    def exec_post_subtask(self):
        """执行恢复后子任务
        """
        LOGGER.info("Start executing restore post subtask ...")
        cache_path = AntDBRestoreService.get_cache_mount_path(self.param_dict)
        # 集群恢复成功，删除备份的conf文件
        tgt_install_path, tgt_data_path = AntDBRestoreService.get_db_install_and_data_path(self.param_dict)
        tgt_obj_extend_info_dict = self.param_dict.get("job", {}).get("targetObject", {}).get("extendInfo", {})
        tgt_db_os_user = tgt_obj_extend_info_dict.get("osUsername", "")
        is_running = AntDBCommonUtils.is_db_running(tgt_db_os_user, tgt_install_path, tgt_data_path)
        if is_running:
            AntDBRestoreService.delete_useless_bak_files(tgt_data_path, self.job_id)
        # 删除patroni日志恢复拷贝数据目录
        AntDBRestoreService.delete_copy_dir_for_patroni(self.param_dict)
        LOGGER.info(f"Execute restore post subtask success.")

        AntDBCommonUtils.write_progress_info(cache_path, RestoreAction.QUERY_RESTORE, RestoreProgress(
            progress=NumberConst.HUNDRED, message="completed"))
        AntDBRestoreService.record_task_result(self.pid, "Executing restore prepare subtask success",
                                               code=ExecuteResultEnum.SUCCESS.value)
